# -*- coding:utf-8 -*-

# @File     :users
# @Author   :Yupeng
# @Date     :2018/10/25 19:08

import enum
import os

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _  # 惰性翻译，在其对象实际用到时才会翻译
from hte.error.models import HTEError, HTEException


class UserRole(enum.IntEnum):
    SYSTEM = -1
    ROOT = 0
    USER_ADMIN = 1
    DATABASE_ADMIN = 2
    DATA_EXPERT = 3
    VERIFIED_USER = 4
    UNVERIFIED_USER = 5

    @property
    def description(self):
        return [_('System'), _('Super Admin'), _('User Admin'), _('Database Admin'),
                _('Data Expert'), _('Verified User'), _('Unverified User')][self + 1]


class User(AbstractUser):
    REQUIRED_FIELDS = ['email', 'tel', 'real_name']
    SEX = [('M', _('Male')), ('F', _('Female')), ('U', _('Unset'))]
    ROLE = [(UserRole.SYSTEM.value, UserRole.SYSTEM.description),
            (UserRole.ROOT.value, UserRole.ROOT.description),
            (UserRole.USER_ADMIN.value, UserRole.USER_ADMIN.description),
            (UserRole.DATABASE_ADMIN.value, UserRole.DATABASE_ADMIN.description),
            (UserRole.DATA_EXPERT.value, UserRole.DATA_EXPERT.description),
            (UserRole.VERIFIED_USER.value, UserRole.VERIFIED_USER.description),
            (UserRole.UNVERIFIED_USER.value, UserRole.UNVERIFIED_USER.description)]
    STATUS = [(0, _('normal')), (1, _('banned')), (2, _('inactive'))]

    username = models.CharField(_('Username'), validators=[UnicodeUsernameValidator()], max_length=100,
                                primary_key=True)
    sex = models.CharField(_('Sex(M|F)'), max_length=5, choices=SEX, default='U')
    real_name = models.CharField(_('Real name'), max_length=254)
    first_name = None
    last_name = None
    email = models.EmailField(_('Email address'), unique=True)
    status = models.IntegerField(_("Account status"), default=0, choices=STATUS)
    email_verified = models.BooleanField(_('Email verified'), default=False)
    institution = models.CharField(_('Institution'), blank=True, max_length=50)
    register_ip = models.CharField(_('Register IP'), max_length=16, blank=True)
    role = models.IntegerField(_('Role'), default=5, choices=ROLE)
    tel = models.CharField(_('Telephone'), max_length=15, blank=True)

    class AvatarSize(enum.Enum):
        LARGE = (200, 200)
        NORMAL = (100, 100)
        SMALL = (30, 30)

    def generate_token(self, account_action):
        """"
        生成操作token
        :param account_action: 账户操作类型，AccountAction枚举类型
        :return: token
        """
        from hte.utils.general import generate_token
        d = {'username': self.username, 'action': account_action}
        if account_action == AccountAction.RESET_PASSWORD:
            d['salt'] = hash(str(self.last_login) + self.password)
        elif account_action == AccountAction.VERIFY_EMAIL:
            d['email'] = self.email
        return generate_token(d)

    def verify_token(self, token, account_action):
        """"
        验证操作token
        :param token: token
        :param account_action:
        :return: token是否有效
        """
        from hte.utils.general import decode_token
        d = decode_token(token)
        if d is None:
            return None
        if d['username'] == self.username and d['action'] == account_action:
            changed = False
            if account_action == AccountAction.VERIFY_EMAIL:
                if self.email_verified or d.get('email', None) != self.email:
                    return None
                if self.role == UserRole.UNVERIFIED_USER:
                    self.role = UserRole.VERIFIED_USER
                self.email_verified = True
                changed = True
            if account_action == AccountAction.RESET_PASSWORD:
                if d.get('salt', None) != hash(str(self.last_login) + self.password):
                    return None
            if changed:
                self.save()
            return d
        else:
            return None

    def to_dict(self, target_user_role=None):
        if target_user_role is None:
            d = self.to_dict_with_fields(User.username, User.sex, User.institution, User.email,
                                         User.email_verified, User.role, User.status, User.real_name, User.tel)
            d['id'] = self.pk
            return d
        elif target_user_role == UserRole.ROOT or target_user_role == UserRole.USER_ADMIN:
            return self.to_dict()

    def to_dict_with_fields(self, *args):
        d = {}
        for arg in args:
            d[arg.field_name] = getattr(self, arg.field_name)
        return d

    def full_clean(self, exclude=None, validate_unique=True):
        try:
            super().full_clean(exclude=exclude, validate_unique=validate_unique)
        except ValidationError as e:
            if 'email' in e.message_dict:
                raise HTEException(HTEError.BAD_EMAIL)
            elif 'username' in e.message_dict:
                return HTEException(HTEError.BAD_USERNAME)
            else:
                raise

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)

    def set_avatar(self, fp):
        from PIL import Image
        image = Image.open(fp)
        w, h = image.size
        if w > h:
            diff = int((w - h) / 2)
            image = image.crop((diff, 0, w - diff, h))
        else:
            diff = int((h - w) / 2)
            image = image.crop((0, diff, w, h - diff))
        if image.mode not in ("RGB",):
            image = image.convert("RGB")
        image = image.resize(User.AvatarSize.LARGE.value, Image.ANTIALIAS)
        image.save(self._avatar_path(User.AvatarSize.LARGE))
        image2 = image.resize(User.AvatarSize.NORMAL.value, Image.ANTIALIAS)
        image2.save(self._avatar_path(User.AvatarSize.NORMAL))
        image3 = image.resize(User.AvatarSize.SMALL.value, Image.ANTIALIAS)
        image3.save(self._avatar_path(User.AvatarSize.SMALL))

    def _avatar_path(self, size=AvatarSize.NORMAL):
        if isinstance(size, str):
            size = size.lower()
        if size == User.AvatarSize.LARGE or size == 'large':
            suffix = 'lg'
        elif size == User.AvatarSize.NORMAL or size == 'normal':
            suffix = 'nm'
        else:
            suffix = 'sm'
        filename = '%s_%s.jpg' % (self.username, suffix)
        # print('_avatar_path----2', filename[0])
        avatar_dir = os.path.join(settings.AVATAR_DIR, filename[0])
        if not os.path.exists(avatar_dir):
            os.makedirs(avatar_dir)
        return os.path.join(avatar_dir, filename)

    def get_avatar(self, size=AvatarSize.NORMAL):
        try:
            with open(self._avatar_path(size), 'rb') as f:
                return f.read()
        except IOError:
            return None

    def get_avatar_uri(self, size=AvatarSize.SMALL):
        path = self._avatar_path(size)
        filename = os.path.basename(path)
        # print(path, filename, '----3')
        if os.path.exists(path):
            # print('----4')
            return os.path.join(settings.MEDIA_URL, 'avatars', self.username[0], filename)
        filename = 'default' + filename[-7:]
        # print('----5')
        return os.path.join(settings.MEDIA_URL, 'avatars', filename)

    @property
    def get_large_avatar_uri(self, size=AvatarSize.LARGE):
        return self.get_avatar_uri(size)

    @property
    def get_normal_avatar_uri(self, size=AvatarSize.NORMAL):
        # print('get_normal_avatar_uri----1')
        return self.get_avatar_uri(size)

    def unread_count(self):
        return self.notifications.filter(unread=True).count()

    @property
    def role_description(self):
        return _(UserRole(self.role).description)

    @property
    def is_hte_admin(self):
        return self.role <= UserRole.DATABASE_ADMIN

    @classmethod
    def user_from_token(cls, token):
        from hte.utils.general import decode_token
        d = decode_token(token)
        if d is None:
            return None
        username = d.get('username', '')
        try:
            return cls.objects.get(username=username)
        except cls.DoesNotExist:
            return None


class LoginHistory(models.Model):
    username = models.ForeignKey(User, on_delete=models.CASCADE)
    login_time = models.DateTimeField(auto_now_add=True)
    ip = models.CharField(max_length=16)


class AccountAction(enum.IntEnum):
    LOGIN = 0
    VERIFY_EMAIL = 1
    RESET_PASSWORD = 2
    DELETE = 3
